#ifndef QDRTESTMASTER_H
#define QDRTESTMASTER_H

#include <QApplication>
#include <QtGui>
#include <QDebug>
#include <QKeyEvent>
#include <QFile>
#include <QTextStream>
#include <QtTest>
#include <QTimer>
#include <QUiLoader>
#include <QFileDialog>
#include "t_timerSender.h"
#include <QDRTestMaster_controlPanel.h>

#define __version "2010.11.20_0400"
/*
 Здесь определяются 2 класса. Мастер тестирования, и агент-фильтр-событий
На последний мы "направляем" все события приложения.

 2010.09.02
Рассширенный класс для тестирования поведения системы через анализ поведени GUI.
Как и QTest, основана на отслеживании/генерации событий QEvent

По сравнению с QTest добавлены следующие вещи:
 - подключается как фильтр событий и отслеживает/записывает события системы.
 - идентифицирует виджеты по именам от корневого до конечного получателя

 - отслеживаются и фильтруются дополнтельные события как QEvent::Move, QEvent::Resize
 - отслеживается/воспроизводится клавиатура с поддержкой русских букв.
    - - воспроизводится также выделение со shift-ом, и сервисные клавиши.

 - может генерировать/воспроизвоить все записанные события.
 - имеет панель управления (запись/вопроизведение событий)

 - поддерживает тестирование свойств виджетов.
 -  - поддерживает "визуальный выбор виджета" для проверки.
 - сценарии записываются/воспроизводятся в/из файлов.

*//*
BUG: существуют проблемы с фильтрацией событий "ресайзинга".
     иногда захватывается окно которое родительское по отношению к нужному.

BUG: Выделение мышью текста "не воспроизводится".
BUG: Контекстное меню не воспроизводится. не "вылазит" и не воспроизводится.


*//*
 TODO: добавить вызов скриптовых вставок в заданные моменты (например для проеки сложных условий
        или доступа к суб-объектам виджета)
 TODO: отследить колесо мыши, перетаскивание, вставку (для записи дествий пользователя)
 TODO: идентификация виджетов по другим свойствам - (надпись, caption, другое)


*/


/*
Имеются трудности с доступом к дочерним объектам отображаемым на некоторых виджетах
- например у QTreeWidget отображаемые объекты не являются виджетами - они как я понимаю "отрисовываются на холсте".
Возможно для разных Классов надо создать разные дополняющие модули что бы можно было получить инфу во что мы "тыкнулись"
 - например для QTreeWidget - это QTreeWidgetItem *itemAt ( int x, int y ) и т.п.

как отслеживать колесо мыши...? событие QWheel

bug Есть проблемы с работой с панелью управления когда появляется модальное окно.
     Кнопки на панели управления просто тановятся не доступны.
*/

typedef QMap<QString, QString> QStringMap; //надо бы потом объеденить 2 типа ))) но потом
typedef QMap<QString, QVariant> QVariantMap; //надо бы потом объеденить 2 типа ))) но потом
#define dateTimeStrFormat "yyyy.MM.dd_hh:mm:ss.zzz"

#define __justMarkFilteredEvents true
#define __filterMouseMoveEvents false
/*
  структура с описанием суб-объекта для сложных комплексных объектов
  которые не отображают агрегированные объекты в виде отдельных виджетов
  таких как например QTreeView, или структурированный текст (элемент DOM-структуры)
*/
struct s_subObjectDescription
    {
        QObject *object;
        QString objectType;
        QString pathName;
        QRect rect;
        QString options;
    };

class QDRTestMaster_eventFilter;

class QDRTestMaster : public QObject
{
    Q_OBJECT
public:
    explicit QDRTestMaster(QObject *parent = 0);

    QDRTestMaster_eventFilter *eventFilter;

    QFile *logfile;
    QTextStream *logStream;
    bool compressMouseMoves;//пробуем урезать движения мыши для того, что бы не плодить объемные логи

public slots:
    void installAppEventHandler();
    // эти 2 функции нужны для того, что бы идентифицировать пользовательские события и потом воспроизводить их повторно.
    // критично к именованию элементов GUI интерфейса!. Важно что бы каждый объект имел имя, причем уникальное в группе
    // подчиненных одному родителю.
    static QString get_namePathOfWidget(QWidget *_cWidget, bool *_ok=0, bool noMessages=false);
    static QWidget *get_widgetByNamePath(QString namePathOfWidget);
    static QString get_valueFromWidget_useOptions(QWidget *_widget, QString actionOptions);
    static QString get_defaultOptionsFromWidget_useMousePos(QWidget *_widget, QPoint localPos);

    static s_subObjectDescription get_objectAndSubNamePathForWidgetDotEvent(QWidget *_widget, int _x, int _y);
    static QObject *get_SubNamePathForWidget(QWidget *_widget, QString _subNamePath);

    static QString get_uniqueNameForChildWidget(QString nameOfWidget, QWidget *parent=0);
        //вернуть имя с суффиктом номера, если такое имя у заданного родителя уже есть в дочерних объектах

    //<<<<<<<<<<
    //блок функций связанный с отслеживанием/записью/воспроизведением действий пользователя.

    int startRecord(QString fileName);
    void stopRecord();

    int startPlay(QString fileName);
    void playNextActionTimerEvent();
    int  playAction(QString actionDescription);
    void stopPlay(QString reason="UserCanceled");

    void highLightWidgetAt(int glX, int glY); //перемещаем специальный виджет поверх того что в заданной точке "самый наверху"
        //используем для выбора виджета у которого будем проверять свойства

    QString lastError();
    int setLastError(QString _newErr);

    static QString escapeAndPrepareValueStringForRecording(QString src);
    static QString dePrepareValueStringFromRecording(QString src);

    int testAction();//4testing only

    void rcv_recordAction(  QDateTime    time,
                          QString      objectNamePath,
                          QString      action, /**/
                          QString      actionOptions
                        ); //

    static Qt::MouseButton String2QtMouseButton(QString button);
    static QString QtMouseButton2String(Qt::MouseButton button);
    static QString bool2string(bool str);
    static bool string2bool(QString str);

    //обработчики виджета контроля
    QWidget *get_controlPanel();
    int rcv_controlPanelFileOpen();
    int rcv_controlPanelRecord();
    int rcv_controlPanelPlay();
    int rcv_controlPanelStop();
    int rcv_controlPanelWSelect(int subMode=0);
    int rcv_getProperties();
    int rcv_controlPanel_propChanged(QString propName);
    int rcv_controlPanelAddTest();
    int rcv_controlPanelAddWait();

    QWidget *get_agentPanel();
    void rcv_agentPanel_linksActivated(QString link);


    /*action,
          mouseMove
          mouseClick
          //mouseDblClick
          mousePress
          mouseRelease
          keyPress
          keyRelease

          //copyForVerify (фиктивное действие для указания места проверки реультата - какого-либо свойства )

          Планируем события генерировать
          (ресайз окошек?)
      */

    /*
      предвижу трудности с qTreeWidget  - клики на элементы отсылаютсч едному и тому-же виджету просмотра скрулл-ареа...
      */
    //>>>>>>>>>>


    static void st_sendTestLog(QString logMessage);
    static void st_clearTestLog();

    //ВЫНЕСТИ В BBot
    static QStringList cleverSplit(QString source, QString splitChar);
    static QStringMap cleverSplitToMap(QString source, QString splitChar, bool trimKeys=false);
    static void setTextOnWidget(QWidget *widg, QString labelName, QString text);
    static QString getTextOnWidget(QWidget *widg, QString labelName);
    static QString escapeCharacters(QString source, QString charToEscape);

    bool waitForWidgetDisAppear(QString widgetName, int timeoutMs=500);
    QWidget *waitForWidgetAppear(QString widgetName, int timeoutMs=500);

    static QDRTestMaster *get_rootTestMaster();

signals:
    void sendTestLog(QString logMessage); //(к панели управления|ко всем)
    void clearTestLog(); //(к панели управления)
    //void exec_ControlPanel();
    //void close_ControlPanel();
    void setSelectWidgetText(QString text);

    void playStarted(QString dateTime, QString fileName);//(ко всем) проигрывание теста начато.
    void playFinished(QString dateTime, QString reason);//(ко всем) проигрывание теста закончено.(С указанием причины)
    void testCheckResult(QString dateTime, bool successfull, QString action, QString notes="");//результат проверки..

private:

    static QDRTestMaster *rootTestMaster;
    QString lastErrorString;

    //QUiLoader uiloader;
    QFile *outfile;
    QTextStream *outStream;
    QFile *infile;
    QTextStream *inStream;


    QDateTime lastDateTime_real;//реальное время - когда мы проиграли послеюнее действие.
    QDateTime lastDateTime_inFile;//записанное время - когда мы зарегистрировали послеюнее проигранное действие.

    int actionCounter;

    QTimer *timer;

    int timeoutMsecToNextAction;
    QString nextActionToPlay; //used then playing
    QString lastRecivedAction; //used then recording
    QString lastRecivedActionType; //used then recording

    QMap<QString,QString> nextActionToPlayOptionMap;
    //QTest *testCore;
    static QWidget *controlPanel;
    static QWidget *agentPanel;

    static QDialog *selectWidget;
    static QDialog *indicateWidget;

    bool controlPanelWidgetSelectingMode;
    int controlPanelWidgetSelectingSubMode;

    //
    QString tmp_objectNamePath;

    //int playTimerId;
    t_timerSender *ttimer;
};

//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
//вспомогательный класс...
class QDRTestMaster_eventFilter : public QObject
{
    Q_OBJECT
  public:
    explicit QDRTestMaster_eventFilter(QDRTestMaster *parent = 0);
    QString filteredWindowName;
  public:
    bool eventFilter(QObject *obj, QEvent *event);
    //int qApp_PlayTimerId;

 signals:
    void sendLog(QString logMessage);

  private:
    QDRTestMaster *testMaster;
    int oldDeltaGxLx,oldDeltaGyLy; //это разница между локальными и глобальными координатами курсора при движении...
    //Как только она начинает меняться - мы понимаем что окно "перетаскивают"
    //что бы понимать что изменение разницы связано с переходом между окнами - помним старый виджет.
    QWidget *oldWidget;
    // на запись в лог идет поправка между маппированием курсора на глобальные координаты и темкакое оно должно быть.
    // оно-же - разница между старой разницей между глобалльными и локальными координатами и новой...

};


#endif // QDRTESTMASTER_H
